Skip to contentMethod: static {...}
1: /*
2: * Copyright (C) 2023 Czech Technical University in Prague
3: *
4: * This program is free software: you can redistribute it and/or modify it under
5: * the terms of the GNU General Public License as published by the Free Software
6: * Foundation, either version 3 of the License, or (at your option) any
7: * later version.
8: *
9: * This program is distributed in the hope that it will be useful, but WITHOUT
10: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11: * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12: * details. You should have received a copy of the GNU General Public License
13: * along with this program. If not, see <http://www.gnu.org/licenses/>.
14: */
15: package cz.cvut.kbss.jopa.model.metamodel;
16:
17: import cz.cvut.kbss.jopa.exception.MetamodelInitializationException;
18: import cz.cvut.kbss.jopa.model.IRI;
19: import cz.cvut.kbss.jopa.model.annotations.MappedSuperclass;
20: import cz.cvut.kbss.jopa.model.annotations.Namespace;
21: import cz.cvut.kbss.jopa.model.annotations.Namespaces;
22: import cz.cvut.kbss.jopa.model.annotations.OWLClass;
23: import cz.cvut.kbss.jopa.utils.NamespaceResolver;
24:
25: import java.lang.reflect.AnnotatedElement;
26: import java.lang.reflect.Modifier;
27: import java.util.Arrays;
28: import java.util.Objects;
29: import java.util.Set;
30: import java.util.stream.Collectors;
31:
32: /**
33: * Utility methods for processing managed types for metamodel construction.
34: */
35: public class ManagedClassProcessor {
36:
37: private ManagedClassProcessor() {
38: throw new AssertionError();
39: }
40:
41: static <T> TypeBuilderContext<T> processManagedType(Class<T> cls) {
42: final NamespaceResolver resolver = detectNamespaces(cls);
43: final AbstractIdentifiableType<T> type;
44: if (isEntityType(cls)) {
45: type = processEntityType(cls, resolver);
46: } else if (isMappedSuperclassType(cls)) {
47: type = processMappedSuperclassType(cls);
48: } else {
49: throw new MetamodelInitializationException("Type " + cls + " is not a managed type.");
50: }
51: return new TypeBuilderContext<>(type, resolver);
52: }
53:
54: /**
55: * Detects namespace declarations relevant to the specified class.
56: * <p>
57: * This means namespaces declared on the class itself, namespaces it inherited from super types, as well as
58: * namespaces declared for the package that contains the specified class.
59: * <p>
60: * Namespaces declared directly by {@link Namespace} as well as in {@link Namespaces} are considered.
61: *
62: * @param cls Class to detect namespaces for
63: * @return Namespace resolver containing detected namespaces
64: */
65: public static <T> NamespaceResolver detectNamespaces(Class<T> cls) {
66: final NamespaceResolver resolver = new NamespaceResolver();
67: detectNamespaces(cls, resolver);
68: return resolver;
69: }
70:
71: /**
72: * Detects namespace declarations relevant to the specified class and registers them with the specified {@link
73: * NamespaceResolver}.
74: * <p>
75: * This means namespaces declared on the class itself, namespaces it inherited from super types, as well as
76: * namespaces declared for the package that contains the specified class.
77: * <p>
78: * Namespaces declared directly by {@link Namespace} as well as in {@link Namespaces} are considered.
79: *
80: * @param cls Class to detect namespaces for
81: * @param namespaceResolver Namespace resolver containing detected namespaces
82: */
83: public static <T> void detectNamespaces(Class<T> cls, NamespaceResolver namespaceResolver) {
84: Objects.requireNonNull(cls);
85: Objects.requireNonNull(namespaceResolver);
86: if (cls.getPackage() != null) {
87: resolveNamespaces(cls.getPackage(), namespaceResolver);
88: }
89: resolveNamespaces(cls, namespaceResolver);
90: }
91:
92: private static void resolveNamespaces(AnnotatedElement target, NamespaceResolver namespaceResolver) {
93: final Namespaces namespaces = target.getDeclaredAnnotation(Namespaces.class);
94: if (namespaces != null) {
95: for (Namespace ns : namespaces.value()) {
96: namespaceResolver.registerNamespace(ns.prefix(), ns.namespace());
97: }
98: }
99: final Namespace namespace = target.getDeclaredAnnotation(Namespace.class);
100: if (namespace != null) {
101: namespaceResolver.registerNamespace(namespace.prefix(), namespace.namespace());
102: }
103: }
104:
105: private static <T> IdentifiableEntityType<T> processEntityType(Class<T> cls, NamespaceResolver namespaceResolver) {
106: final OWLClass c = cls.getDeclaredAnnotation(OWLClass.class);
107: assert c != null;
108:
109: if (cls.isInterface() || Modifier.isAbstract(cls.getModifiers())) {
110: return new AbstractEntityType<>(cls.getSimpleName(), cls, IRI.create(namespaceResolver.resolveFullIri(c.iri())));
111: } else {
112: checkForNoArgConstructor(cls);
113: return new ConcreteEntityType<>(cls.getSimpleName(), cls, IRI.create(namespaceResolver.resolveFullIri(c.iri())));
114: }
115: }
116:
117: private static <T> void checkForNoArgConstructor(Class<T> cls) {
118: try {
119: cls.getDeclaredConstructor();
120: } catch (NoSuchMethodException e) {
121: throw new MetamodelInitializationException("Class " + cls + " is missing required no-arg constructor.", e);
122: }
123: }
124:
125: private static <T> MappedSuperclassTypeImpl<T> processMappedSuperclassType(Class<T> cls) {
126: assert cls.getDeclaredAnnotation(MappedSuperclass.class) != null;
127:
128: return new MappedSuperclassTypeImpl<>(cls);
129: }
130:
131: static <T> Class<? super T> getManagedSuperClass(Class<T> cls) {
132: if (cls.getSuperclass() != null && isManagedType(cls.getSuperclass())) {
133: return cls.getSuperclass();
134: }
135: return null;
136: }
137:
138: public static <T> Set<Class<? super T>> getManagedSuperInterfaces(Class<T> cls) {
139: return Arrays.stream(cls.getInterfaces()).filter(ManagedClassProcessor::isManagedType)
140: .map(clazz -> (Class<? super T>) clazz)
141: .collect(Collectors.toSet());
142: }
143:
144: /**
145: * Checks whether the specified class is a managed type.
146: * <p>
147: * That is, if it is an entity type (annotated with {@link OWLClass}) or a mapped superclass (annotated with {@link
148: * MappedSuperclass}).
149: *
150: * @param cls Class to check
151: * @return {@code true} if the class is a managed type, {@code false} otherwise
152: */
153: public static boolean isManagedType(Class<?> cls) {
154: return isEntityType(cls) || isMappedSuperclassType(cls);
155: }
156:
157: /**
158: * Checks whether the specified class is an entity type.
159: * <p>
160: * An entity is annotated with {@link OWLClass}.
161: *
162: * @param cls Class to check
163: * @return {@code true} if the class is an entity type, {@code false} otherwise
164: */
165: public static boolean isEntityType(Class<?> cls) {
166: return cls != null && cls.getDeclaredAnnotation(OWLClass.class) != null;
167: }
168:
169: /**
170: * Checks whether the specified class is a managed superclass.
171: * <p>
172: * An entity is annotated with {@link MappedSuperclass}.
173: *
174: * @param cls Class to check
175: * @return {@code true} if the class is a mapped superclass, {@code false} otherwise
176: */
177: public static boolean isMappedSuperclassType(Class<?> cls) {
178: return cls != null && cls.getDeclaredAnnotation(MappedSuperclass.class) != null;
179: }
180: }